package edu.siu.math.egut.main;

import java.io.IOException;
import java.util.Scanner;
import java.util.regex.Pattern;

import edu.siu.math.egut.io.Algorithms;
import edu.siu.math.egut.io.LiE;
import edu.siu.math.egut.io.OutputPref;
import edu.siu.math.egut.util.Group;
import edu.siu.math.egut.util.Matrix;
import edu.siu.math.egut.util.NotARootException;
import edu.siu.math.egut.util.Polynomial;
import edu.siu.math.egut.util.Sunipotent;

public class DPR_WriteMathematicaSetup {

    private static final String NOT_A_GROUP = "Third argument not a group.";
    private static final String TRY_AGAIN = "Please try again.\n";
    private static final Pattern TUPLE = Pattern.compile("[\\s*(\\s*\\d\\s*,\\s*)*\\d?]");
    private static final OutputPref HIDE = edu.siu.math.egut.io.OutputPref.HIDE;
    /**
     * @param args
     */
    public static void main(String[] args) {
	try {
	    // check args and get corrections as needed.
	    args= checkArgs(args);
	    
	    // Extract information
	    Group g = Group.getGroup(args[2]);
	    int[][] vRoots = getPieceRoots(args[0], args[1], args[2]);
	    int[][] leviRoots = getLeviRoots( args[1], args[2]);

	    // for each  root a define "x[a,r_]" in Mathematica format.
	    for (int[] alpha : leviRoots) {
		System.out.println("x[{"
			+ Algorithms.tupleToStringNoBrackets(alpha)
			+ "},r_]:="
			+ Matrix.matrixOf(
				Sunipotent.create(alpha, Polynomial.create("r").times(-1)),
				vRoots, g).transpose().toMathematica()
				+";\n\n");

	    }
	    
	    writeTorusDefinition("piece("+args[0]+","+args[1]+","+args[2]+")", g);
	    
	    writeWeylElementDefinitions(g);
	    
	} catch (NotARootException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (IOException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}

    }
    
    /*TODO this method is dependent on a Mathematica setup file that I am using.
     * Remove this dependency.
     * 
     */
    protected static void writeWeylElementDefinitions(Group g) {
	for(int i = 0; i < g.getRank(); i++)
	    System.out.println("w["+Integer.toString(i+1)+"]=s[{"+
		    Algorithms.tupleToStringNoBrackets(g.getPositiveRoots()[i])+"}];\n");
	
    }

    protected static void writeTorusDefinition(String pieceRootListString, Group g ) throws IOException {
	LiE lie = new LiE();
	
	// initialize stringbuilder 
	StringBuilder sb= new StringBuilder();
	
	// finish writing "h[t1_, t2_, ... , tr_]"
	sb.append("h[");
	for( int i = 0; i < g.getRank(); i++)
	    sb.append("t"+Integer.toString(i+1)+"_"+((i==g.getRank()-1)?"]":","));
	
	// the matrix we want can be described in mathematica as 
	// DiagonalMatrix[t1^v1*t2^v2*...*tr^vr],
	/*where r is the rank and for each i, the vector vi is computable 
	 * in LiE as 
	 * ourPiece*Cartan*ei,
	 * with ourPiece being the matrix of our current piece 
	 * (obtain from LiE using the "piece" command)
	 * and ei being the i-th standard basis vector, whcih is also the 
	 * i-th positive root.
	 */
	
	sb.append(":=DiagonalMatrix[");
	
	// 
	for( int i = 0; i < g.getRank(); i++){
	     
	    sb.append("t"+Integer.toString(i+1)+"^(-{"+
		    removeBrackets(
		    lie.exec(pieceRootListString + "*Cartan(" + g.toString() + ")*"
			    + Algorithms.tupleToString(g.getPositiveRoots()[i]), HIDE))+
	    "})"+((i==g.getRank()-1)?"];\n\n":"*"));
	}
	System.out.println(sb.toString());
    }

    private static String removeBrackets(String exec) {
	return exec.trim().replaceAll("\\[", "").replaceAll("\\]","");
    }

    protected static String[] checkArgs(String[] args) throws IOException {
	
	// check that there are three args
	if(args.length != 3){
	    System.out.println(WRONG_NUMBER_OF_ARGS+INSTRUCTIONS);
	}
	
	// check that the third is the name of an exceptional group
	if(Group.getGroup(args[2]) == null){
	    System.out.println(NOT_A_GROUP+INSTRUCTIONS+TRY_AGAIN);
	    args[2] = Algorithms.getGroupName(new Scanner(System.in));
	}
	
	//check that the second specifies a parabolic of the first
	args[1] = validParabolic(args[1], args[2]);
	
	args[0] = validPiece(args[0],args[1],args[2]);

	return args;
    }

    private static String validPiece(String piece, String para,
	    String group) throws IOException {
	return isValidPiece(piece, para, group)? piece : getValidPiece(piece, para, group, new Scanner(System.in));
    }

    private static String getValidPiece(String piece, 
	    String para, String group,
	    Scanner scanner) throws IOException {
	while(!isValidPiece(piece, para, group)){
	    System.out.println("\"piece("+piece+","+para+","+group+")\"  is not working in LiE.  " +
	    		"Please try again.");
	    piece = scanner.next();
	}
	return piece;
    }

    private static boolean isValidPiece(String piece, String para, String group) throws IOException {
	if(TUPLE.matcher(piece).matches()){
	    return new LiE().exec("piece("+piece+","+para+","+group+")", 
		    edu.siu.math.egut.io.OutputPref.HIDE).startsWith("[");
	    
	}
	return false;
	
    }

    private static String validParabolic(String para, String group) throws IOException {
	
	return isValidPara( para, group)? para : getValidPara( para, group, new Scanner(System.in));
    }

    private static boolean isValidPara(String para, String group) throws IOException {
	
	return new LiE().exec("levi("+para+","+group+")", edu.siu.math.egut.io.OutputPref.HIDE).startsWith("[");
    }

    private static String getValidPara(String para, String group,
	    Scanner scanner) throws IOException {
	while(! isValidPara(para, group)){
	    System.out.println("\"levi("+para+","+group+")\" is not working in LiE.  Please try again.");
	    para = scanner.next();
	}
	return para;
    }

    private static int[][] getLeviRoots( String parabolic,
	    String group) throws IOException {
	LiE lie = new LiE();
	return Algorithms.readLiEMatrix(
		lie.exec("levi("+parabolic+","+group+")^-levi("+parabolic+","+group+")",
			edu.siu.math.egut.io.OutputPref.HIDE));
    }

    static int[][] getPieceRoots( 
	    String PieceSpecifier, String ParabolicSpecifier, String groupName) throws IOException {
	LiE lie = new LiE();
	return Algorithms.readLiEMatrix(
		lie.exec(
			"piece("+PieceSpecifier+","	 
			+ParabolicSpecifier+"," +groupName+")",
			edu.siu.math.egut.io.OutputPref.HIDE));
	
	
    }

    private static final String	 INSTRUCTIONS = "Piece Parabolic Group\n Examples:\n\t 1 4 F4" +
    		"for the piece containing [0,0,0,1],\n\t [2,4] [1,5] E8 for the piece containing [2,2,3,4,4,3,2,1]" +
    		"\n\t 2 [1,5] E8 is the sum of [2,0] [1,5] E8, [1,1] [1,5] E8 and [0,2] [1,5] E8.";
    private static final String WRONG_NUMBER_OF_ARGS = "Wrong number of arguments.  Need exactly three.";


}
